package com.xing.binbackend.http.apache;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xing.binbackend.http.RestMethod;
import com.xing.binbackend.http.RestfulParameters;
import com.xing.binbackend.http.TrustAnyTrustManager;
import com.xing.binbackend.http.TrustAnyHostnameVerifier;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
@Slf4j
public class HttpClientRest {
    private final CloseableHttpClient httpClient;
    private final RequestConfig requestConfig;
    private final ObjectMapper mapper =
            new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    public HttpClientRest() throws NoSuchAlgorithmException, KeyStoreException,
            KeyManagementException {
//        SSLContext sslContext = new SSLContextBuilder()
//                .loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true)
//                .build();
//        this.httpClient = HttpClients.custom()
//                .setSSLSocketFactory(new SSLConnectionSocketFactory(
//                        sslContext,
//                        NoopHostnameVerifier.INSTANCE))
//                .build();

//        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null,
//                (TrustStrategy) (chain, authType) -> true).setProtocol("TLS").build();
//        this.httpClient =
//                HttpClients.custom().setSSLSocketFactory(new SSLConnectionSocketFactory
//                (sslContext, new String[]{"TLSv1.2", "TLSv1.3"}, null, NoopHostnameVerifier
//                .INSTANCE)).build();

        SSLContext sslContext = SSLContexts.custom().setProtocol("TLS").build();
        sslContext.init(null, new TrustAnyTrustManager[]{new TrustAnyTrustManager()},
                new SecureRandom());
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
                new String[]{"TLSv1.2"}, null, new TrustAnyHostnameVerifier());
        this.httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        this.requestConfig =
                RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(30000).build();
    }

    public String sendRequest(String url, RestfulParameters parameters, RestMethod method) throws IOException {
        switch (method) {
            case GET:
                return executeGet(url, parameters);
            case POST:
                return executePost(url, parameters);
            default:
                throw new IllegalArgumentException("Unsupported method: " + method);
        }
    }

    // 发送 GET 请求的方法
    private String executeGet(String url, RestfulParameters parameters) throws IOException {
        HttpGet httpGet = new HttpGet(buildUrlWithParams(url, parameters.getParamMap()));
        setHeaders(httpGet, parameters.getHeaderMap());
        httpGet.setConfig(requestConfig);
        return executeRequest(httpGet);
    }

    private String executePost(String url, RestfulParameters parameters) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        setHeaders(httpPost, parameters.getHeaderMap());
        httpPost.setEntity(new UrlEncodedFormEntity(mapToNameValuePairs(parameters.getParamMap())
                , "UTF-8"));
        httpPost.setConfig(requestConfig);
        return executeRequest(httpPost);
    }

    private String executeRequest(HttpRequestBase request) throws IOException {
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            HttpEntity entity = response.getEntity();
            return entity != null ? EntityUtils.toString(entity, "UTF-8") : "";
        } catch (ClientProtocolException e) {
            throw new IOException("HTTP protocol error", e);
        } finally {
            request.reset();
        }
    }

    private List<NameValuePair> mapToNameValuePairs(Map<String, String> params) {
        return params.entrySet().stream().map(e -> new BasicNameValuePair(e.getKey(),
                e.getValue())).collect(Collectors.toList());
    }

    // 设置请求头方法
    private void setHeaders(HttpRequestBase request, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(request::setHeader);
        }
    }

    // GET请求构建URL参数方法
    private String buildUrlWithParams(String baseUrl, Map<String, String> params) throws IOException {
        try {
            URIBuilder uriBuilder = new URIBuilder(baseUrl);
            if (params != null) {
                params.forEach(uriBuilder::addParameter);
            }
            return uriBuilder.build().toString();
        } catch (URISyntaxException e) {
            throw new IOException("Invalid URL syntax", e);
        }
    }

    private <T> Optional<T> convertResp2Bean(HttpResponse response, Class<T> clazz) {
        HttpEntity entity = response.getEntity();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            if (entity == null) {
                log.warn("Response entity is empty");
                return Optional.empty();
            }
            return Optional.ofNullable(mapper.readValue(EntityUtils.toString(entity, "UTF-8"),
                    clazz));
        } catch (IOException e) {
            log.error("Convert response failed: {}", e.getMessage());
            return Optional.empty();
        }
    }
}
